<template>
  <div style="background-color: #fff">
    <el-table
      ref="table"
      v-bind="$attrs"
      :height="tableHeight"
      stripe
      style="width: 100%"
      header-row-class-name="header"
      @selection-change="handleSelectionChange"
      @sort-change="handleSortChange"
      @filter-change="handleFilterChange"
    >
      <el-table-column v-if="selection" type="selection" :with="55" />
      <template v-for="(col, key) in tableColumns" :key="key">
        <el-table-column
          v-if="col.isShow"
          :headerAlign="col.headerAlign ? col.headerAlign : ''"
          :align="col.align ? col.align : 'left'"
          :width="col.width"
          :min-width="col.minWidth"
          :label="col.label"
          :column-key="col.prop"
          :prop="col.prop"
          :sortable="col.sortable"
          :show-overflow-tooltip="col.showOverflowTooltip"
          :formatter="col.formatter"
          :filters="col.filters"
          :fixed="col.fixed"
        >
          <template v-if="col.search === 'text'" #header>
            <span :class="searchClass[col.prop]">{{ col.label }}</span>
            <Search
              v-model:value="searchValue[col.prop]"
              :prop="col.prop"
              :placement="key === 0 ? 'bottom-start' : key === tableColumns.length - 1 ? 'bottom-end' : 'bottom'"
              @search="handleSearch"
            ></Search>
          </template>
          <template v-if="$slots[`column-${col.scope}`]" #default="scope">
            <slot :name="`column-${col.scope}`" :row="scope.row" :column="scope.column" :data="scope.row[col.prop]" />
          </template>
        </el-table-column>
      </template>
      <template v-if="$slots.append">
        <slot name="append"></slot>
      </template>
      <template #empty>
        <span v-if="loading">
          <el-icon :size="15" class="is-loading">
            <Loading />
          </el-icon>
          数据加载中...
        </span>
        <el-empty v-else />
      </template>
    </el-table>
    <el-row v-if="pagination" class="bbar">
      <el-col :span="16">
        <el-pagination
          :small="true"
          :current-page="page.pageNum"
          :page-sizes="[15, 30, 50, 100, 200, 300, 500]"
          :page-size="page.pageSize"
          :layout="props.pageLayout"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        >
        </el-pagination>
      </el-col>
      <el-col :span="8" style="text-align: right; padding-top: 2px">
        <el-popover v-if="props.showColumnTbar" placement="top-end" :width="100" trigger="hover">
          <template #reference>
            <el-button :icon="ScaleToOriginal" size="small" circle></el-button>
          </template>
          <div>
            <span>显示/隐藏列</span>
            <ul class="list-unstyled columns-list">
              <li v-for="(col, key) in tableColumns" :key="key">
                <el-checkbox v-model="col.isShow">{{ col.label }}</el-checkbox>
              </li>
            </ul>
          </div>
        </el-popover>
        <el-tooltip v-if="props.showDownloadTbar" class="item" content="导出数据" placement="top">
          <el-button :icon="Download" size="small" circle @click="handleDownload"></el-button>
        </el-tooltip>
        <el-tooltip v-if="props.showClearFiltersTbar" class="item" content="清除所有过滤条件" placement="top-end">
          <el-button :icon="Refresh" size="small" circle @click="handleClearFilters"></el-button>
        </el-tooltip>
      </el-col>
    </el-row>
    <DownloadDialog
      ref="refDownloadDialog"
      v-model:visible="download.visible"
      :rows="download.rows"
      :cols="tableColumns"
      :total="total"
      :pageSize="page.pageSize"
      :selection="props.selection"
      :pageData="download.pageData"
      :excelName="props.name"
      @export="handleDownloadQuery"
    />
  </div>
</template>

<script setup lang="ts">
import { withDefaults, computed, ref, reactive, onMounted } from 'vue'
import { ElTable, ElTableColumn, ElTooltip, ElPopover, ElEmpty, ElCheckbox, ElPagination } from 'element-plus'
import { ScaleToOriginal, Download, Refresh, Loading } from '@element-plus/icons-vue'
import { util, heightWidth } from '@/utils'
import type { Column } from './types'
import { hooks, PAGE_LAYOUT, PAGE_SIZE } from './hooks'
import Search from './Search.vue'
import DownloadDialog from './Download.vue'

interface Props {
  name?: string
  height?: number
  fullContent?: boolean
  top?: number
  loading?: boolean
  columns: Array<Column>
  selection?: boolean
  pagination?: boolean
  total?: number
  pageSize?: number //定义每页记录数
  pageLayout?: string //定义分页组件的元素，默认total, sizes, prev, pager, next, jumper
  initQuery?: boolean
  initSort?: Record<string, 'ascending' | 'descending'>
  //修改过滤的字段, 例如col显示的是dcName, 过滤时想强制改成dcId过滤, 可配置成 {dcName: dcId}
  filterKeys?: { [key: string]: string }
  showColumnTbar?: boolean //是否显示列显示操作
  showDownloadTbar?: boolean //是否显示下载
  showRemoteDownloadTbar?: boolean //是否显示远程下载
  showClearFiltersTbar?: boolean //是否显示清楚过滤
}

// 定义emit事件
const emits = defineEmits(['query', 'selection-change'])

const { COLUMN_DEFAULT } = hooks()

// 定义属性
const props = withDefaults(defineProps<Props>(), {
  height: 0,
  fullContent: false,
  top: 0,
  loading: false,
  selection: true,
  pagination: true,
  total: 0,
  pageSize: PAGE_SIZE,
  pageLayout: PAGE_LAYOUT,
  initQuery: true,
  showColumnTbar: true,
  showDownloadTbar: true,
  showRemoteDownloadTbar: true,
  showClearFiltersTbar: true
})

const table = ref()

const tableHeight = computed(() => {
  if (props.height > 0) {
    return props.height
  } else if (props.fullContent) {
    return util.getContentHeight() - heightWidth.contentTop - 5 - 28 - heightWidth.contentBottom
  } else if (props.top > 0) {
    //content区域高度 - content区域padding-top - props.top - table与bbar间距(5) - bbar高度(28) - content区域padding-bottom
    return util.getContentHeight() - heightWidth.contentTop - props.top - 5 - 28 - heightWidth.contentBottom
  } else {
    //content区域高度 - content区域padding-top - props.top - table与bbar间距(5) - bbar高度(28) - content区域padding-bottom
    //默认top=32 也就是一个ElButton的高度，通常就是一排button下面接着table的结构
    const top = 32
    return util.getContentHeight() - heightWidth.contentTop - top - 5 - 28 - heightWidth.contentBottom
  }
})

const columnKeyValue: { [key: string]: Column } = {}

const tableColumns = computed(() => {
  const cols: Column[] = reactive<Column[]>([])
  props.columns.forEach((col: Column) => {
    const newCol = Object.assign({}, COLUMN_DEFAULT, col)
    if (newCol.isCreate) {
      if (!col.columnKey) newCol.columnKey = col.prop
      if (col.filters) newCol.search = ''
      if (!col.scope) newCol.scope = col.prop
      cols.push(newCol)
      columnKeyValue[col.prop] = newCol
    }
  })
  return cols
})

const page = reactive<AxiosType.Pagination>({
  pageNum: 1,
  pageSize: props.pageSize,
  sort: {},
  filter: {}
})

const searchClass = computed(() => {
  const cls: { [key: string]: string } = {}
  for (const col in page.filter) {
    cls[col] = 'highlight'
  }
  return cls
})
const searchValue = reactive<{ [key: string]: string }>({})

let initSortObject = ref(props.initSort)
onMounted(() => {
  if (props.initQuery) {
    //处理初始的默认排序
    if (JSON.stringify(page.sort) == '{}' && JSON.stringify(initSortObject.value) != '{}') {
      for (const prop in initSortObject.value) {
        transformSort(prop, initSortObject.value[prop])
      }
      initSortObject.value = {}
    }
    query()
  }
})

const query = () => {
  emits('query', page)
}

const selection = ref([])
const handleSelectionChange = (select: any) => {
  selection.value = select
  emits('selection-change', select)
}

const handleSizeChange = (ps: number) => {
  page.pageSize = ps
  page.pageNum = 1
  query()
}
const handleCurrentChange = (pn: number) => {
  page.pageNum = pn
  query()
}

const handleSortChange = (column: any) => {
  transformSort(column.prop, column.order)
  query()
}

const transformSort = (prop: string, order: any) => {
  page.sort = {}
  if (prop && order) {
    const col = columnKeyValue[prop]
    let key = prop
    if (col && col.isGbk) {
      key = `${key}(gbk)`
    }
    page.sort[key] = order
  }
}

const handleFilterChange = (filters: any) => {
  for (const col in filters) {
    const val = filters[col]
    let key = col
    if (props.filterKeys && props.filterKeys[col]) {
      key = props.filterKeys[col]
    }
    if (val.length > 0) {
      page.filter[key] = val
    } else {
      delete page.filter[key]
    }
  }
  query()
}

const handleSearch = ({ prop }: any) => {
  if (searchValue[prop] && searchValue[prop] !== '') {
    page.filter[prop] = searchValue[prop]
  } else {
    delete page.filter[prop]
  }
  query()
}

const refDownloadDialog = ref()
const download = reactive({ visible: false, rows: undefined, pageData: undefined })
const handleDownload = () => {
  if (!props.showRemoteDownloadTbar) {
    refDownloadDialog.value.downloadLocal(
      tableColumns.value,
      table.value.$refs.tableBody.children[1].children,
      props.selection,
      table.value.data,
      props.name
    )
  } else {
    download.visible = true
    download.rows = table.value.$refs.tableBody.children[1].children
    download.pageData = table.value.data
  }
}
const handleDownloadQuery = (exportParams: AxiosType.Export) => {
  emits('query', { ...page, export: { ...exportParams } })
}

const handleClearFilters = () => {
  for (const prop in searchValue) {
    delete searchValue[prop]
  }
  table.value.clearFilter()
  for (const col in page.filter) {
    delete page.filter[col]
  }
  query()
}

const clearSelection = () => {
  table.value.clearSelection()
}
defineExpose({ table, selection, clearSelection, refresh: query })
</script>

<style lang="scss" scoped>
:deep(table.el-table__header) {
  .header {
    height: 50px;
    & > th {
      background-color: #f5f7fa !important;
    }
  }
}
.bbar {
  margin-top: 5px;
  padding: 0 10px 0 5px;
}
.highlight {
  color: $--color-text-link;
}
.columns-list {
  margin: 0;
  padding-top: 6px;
}
</style>
